﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkLinkedList`1
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections;
using System.Collections.Generic;

#nullable disable
namespace Netick;

/// <summary>
/// A networked linked list collection. This is an ordered list meaning that the elements insertion order is maintained when iterating through the collection.
/// </summary>
public sealed class NetworkLinkedList<T> : 
  NetworkLinkedList,
  INetworkCollection,
  IEnumerable<T>,
  IEnumerable
  where T : unmanaged
{
  private List<T> _original;
  private int _linkedListItemSizeWords;

  public NetworkLinkedList(int capacity)
  {
    this._length = capacity;
    this._original = new List<T>(capacity);
    this._linkedListItemSizeWords = NetickUtils.GetWordSize(sizeof (InternalNetworkLinkedListElement) + sizeof (T));
  }

  public override unsafe void InternalInit(
    INetickNetworkScript beh,
    int* state,
    int elementSizeWords,
    int flags)
  {
    base.InternalInit(beh, state, elementSizeWords, flags);
    this._elementSizeWords = NetickUtils.GetWordSize(sizeof (T));
  }

  internal unsafe void SetElementData(int index, T value)
  {
    this._entity.SetState_Collection((int*) &value, this._intS + 4 + index * this._linkedListItemSizeWords + 2, this._linkedListItemSizeWords - 2, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  internal unsafe void SetElementMeta(int index, InternalNetworkLinkedListElement meta)
  {
    this._entity.SetState_Collection((int*) &meta, this._intS + 4 + index * this._linkedListItemSizeWords, 2, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  internal unsafe InternalNetworkLinkedListElement GetElementMetaPtr(int index)
  {
    return *(InternalNetworkLinkedListElement*) (this._intS + 4 + index * this._linkedListItemSizeWords);
  }

  internal unsafe T GetElementDataPtr(int index)
  {
    return *(T*) (this._intS + 4 + index * this._linkedListItemSizeWords + 2);
  }

  private unsafe InternalNetworkLinkedListElement* IndexToLinkedListElementPointer(int index)
  {
    return (InternalNetworkLinkedListElement*) (this._intS + 4 + index * this._linkedListItemSizeWords);
  }

  public NetworkLinkedListNode<T> GetNode(int index)
  {
    InternalNetworkLinkedListElement elementMetaPtr = this.GetElementMetaPtr(index);
    return new NetworkLinkedListNode<T>()
    {
      Item = this.GetElementDataPtr(index),
      Index = index,
      Previous = elementMetaPtr.Previous,
      Next = elementMetaPtr.Next
    };
  }

  internal unsafe int HeadNode
  {
    get => *this._intS;
    set
    {
      this._entity.SetState_Collection(&value, this._intS, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  internal unsafe int TailNode
  {
    get => this._intS[1];
    set
    {
      this._entity.SetState_Collection(&value, this._intS + 1, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  internal unsafe int FreeTailNode
  {
    get => this._intS[2];
    set
    {
      this._entity.SetState_Collection(&value, this._intS + 2, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  /// <summary>Gets the first node in the linked list.</summary>
  public NetworkLinkedListNode<T> Head
  {
    get
    {
      if (this.HeadNode != -1)
        return this.GetNode(this.HeadNode);
      return new NetworkLinkedListNode<T>()
      {
        Index = -1,
        Next = -1,
        Previous = -1
      };
    }
  }

  /// <summary>Gets the last node in the linked list.</summary>
  public NetworkLinkedListNode<T> Tail
  {
    get
    {
      if (this.TailNode != -1)
        return this.GetNode(this.TailNode);
      return new NetworkLinkedListNode<T>()
      {
        Index = -1,
        Next = -1,
        Previous = -1
      };
    }
  }

  /// <summary>Gets the number of items contained in the linked list.</summary>
  public unsafe int Count
  {
    get => (IntPtr) this._intS == IntPtr.Zero ? this._original.Count : this._intS[3];
    internal set
    {
      this._entity.SetState_Collection(&value, this._intS + 3, 1, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  /// <summary>Adds an item to the end of the linked list.</summary>
  /// <param name="item">The item to add.</param>
  /// <returns>True if the item was added; otherwise, false.</returns>
  public unsafe bool Add(T item)
  {
    if ((IntPtr) this._intS == IntPtr.Zero)
    {
      this._original.Add(item);
      return true;
    }
    int index = this.PopFree();
    if (index == -1)
      return false;
    InternalNetworkLinkedListElement elementMetaPtr = this.GetElementMetaPtr(index) with
    {
      Previous = this.TailNode,
      Next = -1
    };
    this.SetElementMeta(index, elementMetaPtr);
    if (this.TailNode == -1)
      this.HeadNode = index;
    else
      this.SetElementMeta(this.TailNode, this.GetElementMetaPtr(this.TailNode) with
      {
        Next = index
      });
    this.TailNode = index;
    this.SetElementData(index, item);
    ++this.Count;
    return true;
  }

  /// <summary>Adds an item as the first node in the linked list.</summary>
  /// <param name="item">The item to add.</param>
  /// <param name="addedNode">The node that was added.</param>
  /// <returns>True if the item was added; otherwise, false.</returns>
  public bool AddFirst(T item, out NetworkLinkedListNode<T> addedNode)
  {
    addedNode = new NetworkLinkedListNode<T>();
    bool flag;
    if (this.HeadNode != -1)
    {
      flag = this.AddBefore(this.Head, item, out addedNode);
    }
    else
    {
      flag = this.Add(item);
      if (flag)
        addedNode = this.GetNode(this.HeadNode);
    }
    return flag;
  }

  /// <summary>Adds an item as the last node in the linked list.</summary>
  /// <param name="item">The item to add.</param>
  /// <param name="addedNode">The node that was added.</param>
  /// <returns>True if the item was added; otherwise, false.</returns>
  public bool AddLast(T item, out NetworkLinkedListNode<T> addedNode)
  {
    addedNode = new NetworkLinkedListNode<T>();
    int num = this.Add(item) ? 1 : 0;
    if (num == 0)
      return num != 0;
    addedNode = this.GetNode(this.TailNode);
    return num != 0;
  }

  /// <summary>
  /// Adds an item before the specified node in the linked list.
  /// </summary>
  /// <param name="beforeThisNode">The node before which to insert the new item.</param>
  /// <param name="item">The item to add.</param>
  /// <param name="addedNode">The node that was added.</param>
  /// <returns>True if the item was added; otherwise, false.</returns>
  public bool AddBefore(
    NetworkLinkedListNode<T> beforeThisNode,
    T item,
    out NetworkLinkedListNode<T> addedNode)
  {
    addedNode = new NetworkLinkedListNode<T>();
    int index = this.PopFree();
    if (index == -1 || beforeThisNode.Index == -1)
      return false;
    InternalNetworkLinkedListElement elementMetaPtr1 = this.GetElementMetaPtr(beforeThisNode.Index);
    InternalNetworkLinkedListElement elementMetaPtr2 = this.GetElementMetaPtr(index) with
    {
      Previous = elementMetaPtr1.Previous,
      Next = beforeThisNode.Index
    };
    this.SetElementMeta(index, elementMetaPtr2);
    int previous = elementMetaPtr1.Previous;
    elementMetaPtr1.Previous = index;
    this.SetElementMeta(beforeThisNode.Index, elementMetaPtr1);
    if (previous != -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr3 = this.GetElementMetaPtr(previous) with
      {
        Next = index
      };
      this.SetElementMeta(previous, elementMetaPtr3);
    }
    if (this.HeadNode == beforeThisNode.Index)
      this.HeadNode = index;
    this.SetElementData(index, item);
    ++this.Count;
    addedNode.Item = item;
    addedNode.Index = index;
    addedNode.Previous = elementMetaPtr2.Previous;
    addedNode.Next = elementMetaPtr2.Next;
    return true;
  }

  /// <summary>
  /// Adds an item after the specified node in the linked list.
  /// </summary>
  /// <param name="afterThisNode">The node after which to insert the new item.</param>
  /// <param name="item">The item to add.</param>
  /// <param name="addedNode">The node that was added.</param>
  /// <returns>True if the item was added; otherwise, false.</returns>
  public bool AddAfter(
    NetworkLinkedListNode<T> afterThisNode,
    T item,
    out NetworkLinkedListNode<T> addedNode)
  {
    addedNode = new NetworkLinkedListNode<T>();
    int index = this.PopFree();
    if (index == -1 || afterThisNode.Index == -1)
      return false;
    InternalNetworkLinkedListElement elementMetaPtr1 = this.GetElementMetaPtr(afterThisNode.Index);
    InternalNetworkLinkedListElement elementMetaPtr2 = this.GetElementMetaPtr(index) with
    {
      Previous = afterThisNode.Index,
      Next = elementMetaPtr1.Next
    };
    this.SetElementMeta(index, elementMetaPtr2);
    int next = elementMetaPtr1.Next;
    elementMetaPtr1.Next = index;
    this.SetElementMeta(afterThisNode.Index, elementMetaPtr1);
    if (next != -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr3 = this.GetElementMetaPtr(next) with
      {
        Previous = index
      };
      this.SetElementMeta(next, elementMetaPtr3);
    }
    if (this.TailNode == afterThisNode.Index)
      this.TailNode = index;
    this.SetElementData(index, item);
    ++this.Count;
    addedNode.Item = item;
    addedNode.Index = index;
    addedNode.Previous = elementMetaPtr2.Previous;
    addedNode.Next = elementMetaPtr2.Next;
    return true;
  }

  /// <summary>
  /// Removes the first occurrence of the specified item from the linked list.
  /// </summary>
  /// <param name="item">The item to remove.</param>
  /// <returns>True if the item was removed; otherwise, false.</returns>
  public unsafe bool Remove(T item)
  {
    if ((IntPtr) this._intS == IntPtr.Zero)
      return false;
    int removedIndex = -1;
    for (int index = this.HeadNode; index != -1; index = this.GetElementMetaPtr(index).Next)
    {
      if (EqualityComparer<T>.Default.Equals(item, this.GetElementDataPtr(index)))
      {
        removedIndex = index;
        break;
      }
    }
    if (removedIndex != -1)
      this.InternalRemove(removedIndex, true);
    return removedIndex != -1;
  }

  /// <summary>Removes the specified node from the linked list.</summary>
  /// <param name="node">The node to remove.</param>
  /// <returns>True if the node was removed; otherwise, false.</returns>
  public bool Remove(NetworkLinkedListNode<T> node)
  {
    if (node.Index != -1)
      this.InternalRemove(node.Index, true);
    return node.Index != -1;
  }

  /// <summary>Removes all items from the linked list.</summary>
  public void Clear()
  {
    int count = this.Count;
    Span<int> span = stackalloc int[count];
    int index1 = this.HeadNode;
    int num = 0;
    while (index1 != -1)
    {
      span[num] = index1;
      index1 = this.GetElementMetaPtr(index1).Next;
      ++num;
    }
    for (int index2 = 0; index2 < count; ++index2)
      this.InternalRemove(span[index2], false);
    this.Count = 0;
  }

  private bool InternalRemove(int removedIndex, bool decrementCount)
  {
    if (removedIndex == -1)
      return false;
    InternalNetworkLinkedListElement elementMetaPtr1 = this.GetElementMetaPtr(removedIndex);
    if (elementMetaPtr1.Previous != -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr2 = this.GetElementMetaPtr(elementMetaPtr1.Previous) with
      {
        Next = elementMetaPtr1.Next
      };
      this.SetElementMeta(elementMetaPtr1.Previous, elementMetaPtr2);
    }
    if (elementMetaPtr1.Next != -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr3 = this.GetElementMetaPtr(elementMetaPtr1.Next) with
      {
        Previous = elementMetaPtr1.Previous
      };
      this.SetElementMeta(elementMetaPtr1.Next, elementMetaPtr3);
    }
    if (this.HeadNode == removedIndex)
      this.HeadNode = elementMetaPtr1.Next;
    if (this.TailNode == removedIndex)
      this.TailNode = elementMetaPtr1.Previous;
    this.PushFree(removedIndex);
    if (decrementCount)
      --this.Count;
    return true;
  }

  private void PushFree(int free)
  {
    int freeTailNode = this.FreeTailNode;
    if (freeTailNode == -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr = this.GetElementMetaPtr(free) with
      {
        Next = -1,
        Previous = -1
      };
      this.SetElementMeta(free, elementMetaPtr);
    }
    else
    {
      InternalNetworkLinkedListElement elementMetaPtr1 = this.GetElementMetaPtr(free) with
      {
        Next = -1,
        Previous = this.FreeTailNode
      };
      InternalNetworkLinkedListElement elementMetaPtr2 = this.GetElementMetaPtr(freeTailNode) with
      {
        Next = free
      };
      this.SetElementMeta(freeTailNode, elementMetaPtr2);
      this.SetElementMeta(free, elementMetaPtr1);
    }
    this.FreeTailNode = free;
  }

  private int PopFree()
  {
    int freeTailNode = this.FreeTailNode;
    if (freeTailNode == -1)
      return -1;
    InternalNetworkLinkedListElement elementMetaPtr1 = this.GetElementMetaPtr(freeTailNode);
    if (elementMetaPtr1.Previous != -1)
    {
      InternalNetworkLinkedListElement elementMetaPtr2 = this.GetElementMetaPtr(elementMetaPtr1.Previous) with
      {
        Next = -1
      };
      this.SetElementMeta(elementMetaPtr1.Previous, elementMetaPtr2);
      this.FreeTailNode = elementMetaPtr1.Previous;
    }
    else
      this.FreeTailNode = -1;
    return freeTailNode;
  }

  public override unsafe void InternalReset()
  {
    *this._intS = -1;
    this._intS[1] = -1;
    this._intS[2] = this._length - 1;
    this._intS[3] = 0;
    int index1 = -1;
    for (int index2 = 0; index2 < this._length; ++index2)
    {
      InternalNetworkLinkedListElement linkedListElement = new InternalNetworkLinkedListElement();
      linkedListElement.Previous = index1;
      linkedListElement.Next = -1;
      if (index1 != -1)
        this.IndexToLinkedListElementPointer(index1)->Next = index2;
      index1 = index2;
      *this.IndexToLinkedListElementPointer(index2) = linkedListElement;
    }
    for (int index3 = 0; index3 < this._original.Count; ++index3)
      this.Add(this._original[index3]);
  }

  /// <summary>
  /// Gets an enumerable collection of all nodes in the linked list.
  /// </summary>
  public IEnumerable<NetworkLinkedListNode<T>> Nodes
  {
    get
    {
      for (int cur = this.HeadNode; cur != -1; cur = this.GetElementMetaPtr(cur).Next)
      {
        InternalNetworkLinkedListElement elementMetaPtr = this.GetElementMetaPtr(cur);
        yield return new NetworkLinkedListNode<T>()
        {
          Item = this.GetElementDataPtr(cur),
          Index = cur,
          Previous = elementMetaPtr.Previous,
          Next = elementMetaPtr.Next
        };
      }
    }
  }

  public IEnumerator<T> GetEnumerator()
  {
    NetworkLinkedList<T> networkLinkedList = this;
    int i;
    if (networkLinkedList._entity == null)
    {
      for (i = 0; i < networkLinkedList._original.Count; ++i)
        yield return networkLinkedList._original[i];
    }
    else
    {
      for (i = networkLinkedList.HeadNode; i != -1; i = networkLinkedList.GetElementMetaPtr(i).Next)
        yield return networkLinkedList.GetElementDataPtr(i);
    }
  }

  IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();
}
